/*
This file implements distributed nachos server
There are up to 5 servers
*/

#include "time.h"

#include "copyright.h"

#include "system.h"
#include "network.h"
#include "post.h"
#include "interrupt.h"
#include "bitmap.h"
#include "synch.h"
#include "time.h"
#include <sys/time.h>
#include <iostream>

using namespace std;

#define MAX_LOCK 200
#define MAX_CV 200
#define MAX_MV 200
#define MAX_THREAD 50
#define MAX_MAILBOX 20
#define MAX_CLIENT 1000

Semaphore mutex("mutex", 1);

struct CommandType {
	int type;        // 1,2,3 for creates
	int fail;        // fail count
	char* name;   // name, only for create
	int command;     // command index
	int failmessage; // first byte of fail message
	int mid;         // client id
	int mb;          // client mailbox
	long long timestamp;   // message time
	int size;
	CommandType *next;
}*commandQueue = NULL;

int currentCommand = 0;

struct ConditionQueue {
	int cvid;          // condition id
	int lockid;        // lock id
	int mid;           // client machine id
	int mb;            // client mail box
	int command;       // command id
	int success;       // total success number
	int fail;          // fail counter
	int confirm;       // success counter
	ConditionQueue *next;	
}*cvQueue = NULL;

struct NotifyQueue {
	int type;    // 1 2 3 for 3 creates
	char* name;  // variable name
	int mid;
	int mb;
	int from;
	int command;
	NotifyQueue* next;
}*notifyQueue = NULL;

struct ServerLock {
	char name[50];
	BitMap *regClient;
	List *queue;
	int holder;
	int index;
} locks[MAX_LOCK];

int lockCount = 0;

struct ServerCV {
	char name[50];
	BitMap *regClient;
	List *queue;
	int index;
} cvs[MAX_CV];

struct CVQueueElement {
	int id;
	int lock;	
};

int cvCount = 0;

struct ServerMV {
	char name[50];
	BitMap *regClient;
	int *value;
	int size;
	int index;
} mvs[MAX_MV];
	
int mvCount = 0;

// copy byte array
void ccopy(char *des, char* src, int size) {
	for(int i = 0; i < size; i++, des++, src++) {
		*des = *src;
	}
}

// integer to array
int itoa(int x, char *m) {
	char buffer[MaxMailSize];
	int count = 0;
	do {
		buffer[count++] = x % 10;
		x /= 10;
	} while(x);
	for(int i = 0; i < count; i++) {
		m[i] = buffer[count-1-i]+'0';
		DEBUG('N', "%d %c\n",i, m[i]);
	}
	return count;
}

// integer to array
int itoa(long long x, char *m) {
	char buffer[MaxMailSize];
	int count = 0;
	do {
		buffer[count++] = x % 10;
		x /= 10;
	} while(x);
	for(int i = 0; i < count; i++) {
		m[i] = buffer[count-1-i]+'0';
		DEBUG('N', "%d %c\n",i, m[i]);
	}
	return count;
}

// array to integer
int atoi(char *m, int size) {
	int ret = 0;
	for(int i = 0; i < size; i++, m++) {
		DEBUG('N', "%d %d\n",i, m[i]);
		
		ret = ret * 10 + (*m) - '0';
	}
	return ret;
}

long long atoi(char *m, int size, int dummy) {
	long long ret = 0;
	for(int i = 0; i < size; i++, m++) {
		DEBUG('N', "%d %d\n",i, m[i]);
		
		ret = ret * 10 + (*m) - '0';
	}
	return ret;
}

// handle create lock 
int CreateLock(char* name, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	for(int i = 0; i < lockCount; i++) {
		if (!strcmp(name, locks[i].name)) {
			// lock exists
			locks[i].regClient->Mark(id);
			return i;
		}
	}
	// create new lock
	int lock_index = lockCount * 5 + server;
	strcpy(locks[lockCount].name, name);
	locks[lockCount].regClient = new BitMap(MAX_CLIENT);
	locks[lockCount].holder = 0;
	locks[lockCount].queue = new List;
	locks[lockCount].regClient->Mark(id);
	locks[lockCount].index = lock_index;
	lockCount++;
	return lock_index;
}

// handle register lock
int CreateLock(int lock_index, int mid, int mb) {
	printf("Register Lock %d %d %d.\n", lock_index, mid, mb);
	int id = mid * MAX_MAILBOX + mb;
	locks[lock_index].regClient->Mark(id);
	return locks[lock_index].index;
}

// test lock
bool TestLockHolder(int lock_index, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	return locks[lock_index].holder == id;
}

// get current time
long long GetTime() {
	timeval tim;
  	gettimeofday(&tim, NULL);
  	long long t = tim.tv_sec*1000000 + tim.tv_usec;
	// printf("Time : %lld\n", t);
	cout << "time " << t<< endl;
	return t;
}

// handle acquire
void Acquire(int lockid, int mid, int mb) {
	DEBUG('N', "Acquire. %d\n", lockid);
	int id = mid * MAX_MAILBOX + mb;
	printf("ACQUIRE ID %d.\n",id);
	// check lock is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (lockid < 0 || lockid >= lockCount || !locks[lockid].regClient->Test(id)) {
		// in multi server, this may never be reached
		// lock is not valid
		DEBUG('N', "Not valid. %d\n", lockid);
		printf("Not Valid.\n");
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else if (locks[lockid].holder <= 0 || locks[lockid].holder == id) {
		// lock is available
		DEBUG('N', "avaliable. %d\n", lockid);
		locks[lockid].holder = id;
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// lock is held by other machine
		// put id in the queue
		// do not send anything
		printf("ACQUIRE WAITING.\n");
		locks[lockid].queue->Append((void*)(id+MAGIC_NUMBER));
	}
}


// handle release
void Release(int lockid, int mid, int mb) {
	DEBUG('N', "Release.\n");
	int id = mid * MAX_MAILBOX + mb;
	// check lock is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (lockid < 0 || lockid >= lockCount || !locks[lockid].regClient->Test(id)) {
		// lock is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else if (locks[lockid].holder != id) {
		// lock is not belong to the process
		// just confirm back
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// lock is held by this process
		// wake up a machine in the queue
		int v = (int)locks[lockid].queue->Remove() - MAGIC_NUMBER;
		locks[lockid].holder = v;
		DEBUG('N', "Release %d %d\n", lockid, v);
		if (v > 0) {
			// other thread acquires the lock
			message[0] = 0;
			outPktHdr.to = v / MAX_MAILBOX;
			outMailHdr.to = v % MAX_MAILBOX;
			outMailHdr.length = 1;
			postOffice->Send(outPktHdr, outMailHdr, message);
		}
		// send release back
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}
}


// handle release without confirm back to the lock holder
void ReleaseWithoutConfirm(int lockid, int mid, int mb) {
	DEBUG('N', "Release.\n");
	int id = mid * MAX_MAILBOX + mb;
	// check lock is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (lockid < 0 || lockid >= lockCount || !locks[lockid].regClient->Test(id)) {
		// lock is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else if (locks[lockid].holder != id) {
		// lock is not belong to the process
		// just confirm back
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// lock is held by this process
		// wake up a machine in the queue
		int v = (int)locks[lockid].queue->Remove() - MAGIC_NUMBER;
		locks[lockid].holder = v;
		DEBUG('N', "Release %d %d\n", lockid, v);
		if (v > 0) {
			// other thread acquires the lock
			message[0] = 0;
			outPktHdr.to = v / MAX_MAILBOX;
			outMailHdr.to = v % MAX_MAILBOX;
			outMailHdr.length = 1;
			postOffice->Send(outPktHdr, outMailHdr, message);
		}
	}
}

// destroy lock
void DestroyLock(int lockid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check lock is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	DEBUG('l', "Destory Lock %d %d.\n",lockid, id);
	char message[2];
	
	if (lockid < 0 || lockid >= lockCount || !locks[lockid].regClient->Test(id)) {
		// lock is not valid
		DEBUG('l', "Not Valid.\n");
		
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else if (locks[lockid].holder != id) {
		// lock is not belong to the process
		// just confirm back		
		message[0] = 0;
		message[1] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 2;
		locks[lockid].regClient->Clear(id);
		DEBUG('l', "Message %d.\n", message[0]);
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// lock is held by this process
		// wake up a machine in the queue
		int v = (int)locks[lockid].queue->Remove() - MAGIC_NUMBER;
		locks[lockid].holder = v;
		if (v > 0) {
			// other thread acquires the lock
			message[0] = 0;
			outPktHdr.to = v / MAX_MAILBOX;
			outMailHdr.to = v % MAX_MAILBOX;
			outMailHdr.length = 1;
			postOffice->Send(outPktHdr, outMailHdr, message);
		}
		// send release back
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		locks[lockid].regClient->Clear(id);
		postOffice->Send(outPktHdr, outMailHdr, message);
	}
}

// create condition
int CreateCondition(char* name, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// create new lock
	for(int i = 0; i < cvCount; i++) {
		if (!strcmp(name, cvs[i].name)) {
			// lock exists
			cvs[i].regClient->Mark(id);
			return cvs[i].index;
		}
	}
	int cv_index = cvCount * 5 + server;
	strcpy(cvs[cvCount].name, name);
	
	cvs[cvCount].regClient = new BitMap(MAX_CLIENT);
	cvs[cvCount].queue = new List;
	cvs[cvCount].regClient->Mark(id);
	cvs[cvCount].index = cv_index;
	cvCount++;
	return cv_index;
}

// register condition
int CreateCondition(int cv_index, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	printf("Register CV %d %d %d.\n", cvs[cv_index].index, mid, mb);
	cvs[cv_index].regClient->Mark(id);
	return cvs[cv_index].index;
}

// destroy condition
void DestroyCV(int cvid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (cvid < 0 || cvid >= cvCount || !cvs[cvid].regClient->Test(id)) {
		// mv is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		cvs[cvid].regClient->Clear(id);
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}	
}

// wait on a condition
void Wait(int cvid, int lockid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (cvid < 0 || lockid < 0 || cvid >= cvCount || !cvs[cvid].regClient->Test(id) || 
		lockid >= lockCount || !locks[lockid].regClient->Test(id) ||
		locks[lockid].holder != id) {
		// cv or lock is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// release the lock
		int v = (int)locks[lockid].queue->Remove() - MAGIC_NUMBER;
		locks[lockid].holder = v;
		DEBUG('N', "Release %d %d\n", lockid, v);
		printf("Wait %s.\n", cvs[cvid].name);
		if (v > 0) {
			// other thread acquires the lock
			message[0] = 0;
			outPktHdr.to = v / MAX_MAILBOX;
			outMailHdr.to = v % MAX_MAILBOX;
			outMailHdr.length = 1;
			postOffice->Send(outPktHdr, outMailHdr, message);
		}
		// put thread in the wait queue
		CVQueueElement *t = new CVQueueElement;
		t->id = id;
		t->lock = lockid;
		cvs[cvid].queue->Append((void*)t);
		// do not send anything now
	}	
}

// singal the condition
void Signal(int cvid, int lockid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (cvid < 0 || lockid < 0 || cvid >= cvCount || !cvs[cvid].regClient->Test(id) || 
		lockid >= lockCount || !locks[lockid].regClient->Test(id) ||
		locks[lockid].holder != id) {
		// cv or lock is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// wake one up
		CVQueueElement *t = (CVQueueElement *)cvs[cvid].queue->Remove();
		List *l = new List;
		if (t != NULL) {
			// match the lock
			while (t->lock != lockid) {
				l->Append((void*)t);
				t = (CVQueueElement *)cvs[cvid].queue->Remove();
				if (t == NULL) break;
			}
			void *tt = l->Remove();
			while (tt != NULL) {
				cvs[cvid].queue->Prepend(tt);
				tt = l->Remove();
			}
			if (t != NULL) {
				DEBUG('L', "Wake one Up.\n");
				int v = t->id;
				Acquire(lockid, v / MAX_MAILBOX, v % MAX_MAILBOX);
				delete t;
			}
		}
		delete l;
		
		// return message
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}	
}

// broadcast the condition
void Broadcast(int cvid, int lockid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (cvid < 0 || lockid < 0 || cvid >= cvCount || !cvs[cvid].regClient->Test(id) || 
		lockid >= lockCount || !locks[lockid].regClient->Test(id) ||
		locks[lockid].holder != id) {
		// cv or lock is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// wake all up
		printf("Broadcast %s.\n", cvs[cvid].name);
		CVQueueElement *t = (CVQueueElement *)cvs[cvid].queue->Remove();
		List *l = new List;
		while (t != NULL) {
			if (t->lock != lockid) {
				l->Append((void*)t);
			} else {
				int v = t->id;
				Acquire(lockid, v / MAX_MAILBOX, v % MAX_MAILBOX);
				delete t;
			}
			t = (CVQueueElement *)cvs[cvid].queue->Remove();
		}
		void *tt = l->Remove();
		while (tt != NULL) {
			cvs[cvid].queue->Prepend(tt);
			tt = l->Remove();
		}
		delete l;
		
		// return message
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}
}

// create monitor
int CreateMV(char* name, int size, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	for(int i = 0; i < mvCount; i++) {
		if (!strcmp(name, mvs[i].name)) {
			// mv exists
			mvs[i].regClient->Mark(id);
			return mvs[i].index;
		}
	}
	// create new mv
	int mv_index = mvCount * 5 + server;
	strcpy(mvs[mvCount].name, name);
	mvs[mvCount].regClient = new BitMap(MAX_CLIENT);
	mvs[mvCount].regClient->Mark(id);
	mvs[mvCount].index = mv_index;
	mvs[mvCount].value = new int[size];
	mvs[mvCount].size = size;
	for(int i = 0; i < size; i++) mvs[mvCount].value[i] = 0;
	mvCount++;
	return mv_index;
}

// register monitor
int CreateMV(int mv_index, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	printf("Register MV %d %d %d %d.\n", mv_index, mvs[mv_index].index, mid, mb);
	mvs[mv_index].regClient->Mark(id);
	return mvs[mv_index].index;
}

// destory monitor
void DestroyMV(int mvid, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	
	if (mvid < 0 || mvid >= mvCount || !mvs[mvid].regClient->Test(id)) {
		// mv is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		mvs[mvid].regClient->Clear(id);
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}	
}

// read mv value
void ReadMV(int mvid, int index, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	printf("ReadMV %s %d %d %d\n", mvs[mvid].name, index, mid, mb);
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[100];
	if (mvid < 0 || mvid >= mvCount || !mvs[mvid].regClient->Test(id) || index >= mvs[mvid].size) {
		// mv is not valid
		printf("MV is not valid\n");
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// read the mv value
		DEBUG('R', "%d\n",mvs[mvid].value[index]);
		message[0] = itoa(mvs[mvid].value[index], message+1);
		printf("MV is valid %d.\n", message[0]);
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = message[0]+1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}
}

// set mv value
void SetMV(int mvid, int index, int value, int mid, int mb) {
	int id = mid * MAX_MAILBOX + mb;
	// check mv is belong to this machine's thread.
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[2];
	if (mvid < 0 || mvid >= mvCount || !mvs[mvid].regClient->Test(id) || index >= mvs[mvid].size) {
		// mv is not valid
		message[0] = 1;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	} else {
		// set the mv value
		mvs[mvid].value[index] = value;
		message[0] = 0;
		outPktHdr.to = mid;
		outMailHdr.to = mb;
		outMailHdr.length = 1;
		postOffice->Send(outPktHdr, outMailHdr, message);
	}
}


// find lock index by id
int FindLock(int index) {
	for(int i = 0; i < lockCount; i++) {
		if (locks[i].index == index) return i;
	}
	return -1;
}

// find lock index by name
int FindLock(char* name) {
	for(int i = 0; i < lockCount; i++) {
		if (!strcmp(locks[i].name,name)) return i;
	}
	return -1;
}

// find cv index by id
int FindCV(int index) {
	for(int i = 0; i < cvCount; i++) {
		if (cvs[i].index == index) return i;
	}
	return -1;
}

// find cv index by name
int FindCV(char* name) {
	for(int i = 0; i < cvCount; i++) {
		if (!strcmp(cvs[i].name,name)) return i;
	}
	return -1;
}

// find mv index by index
int FindMV(int index) {
	for(int i = 0; i < mvCount; i++) {
		if (mvs[i].index == index) return i;
	}
	return -1;
}

// find mv index by name
int FindMV(char* name) {
	for(int i = 0; i < mvCount; i++) {
		if (!strcmp(mvs[i].name,name)) return i;
	}
	return -1;
}

// message to the servers
// helper function
void Message(int mid, int command, int success) {
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[MaxMailSize];
	outMailHdr.to = 1;
	outPktHdr.to = mid;
	message[0] = command/100;
	message[1] = command%100;
	message[2] = message[3] = 0;
	message[4] = success;
	outMailHdr.length = 5;
	printf("send success/fail message to %d:%d:%d.\n", mid, 1, success);
	postOffice->Send(outPktHdr, outMailHdr, message);
}

// message to the servers
// helper function
void Message(int server, int mid, int mb, int command, int success) {
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[MaxMailSize];
	outMailHdr.to = 1;
	outPktHdr.to = server;
	message[0] = command/100;
	message[1] = command%100;
	message[2] = mid;
	message[3] = mb;
	message[4] = success;
	outMailHdr.length = 5;
	printf("send success/fail message to %d:%d:%d.\n", server, 1, success);
	postOffice->Send(outPktHdr, outMailHdr, message);
}

// message to the servers
// helper function
void LockMessage(int command, int mid, int mb, int lockid, int reply, int to) {
	PacketHeader outPktHdr;
    MailHeader outMailHdr;
	char message[MaxMailSize];
	outMailHdr.to = 1;
	outPktHdr.to = to;
	message[0] = command/100;
	message[1] = command%100;
	message[2] = mid;
	message[3] = mb;
	message[4] = 2;
	message[5] = lockid/100;
	message[6] = lockid%100;
	message[7] = reply;
	outMailHdr.length = 8;
	postOffice->Send(outPktHdr, outMailHdr, message);
}

// server for client
void ServerForClient(int numberOfServer) {
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;
    char buffer[MaxMailSize];
	char message[MaxMailSize];
	char name[MaxMailSize];
	int lockid;
	int cvid;
	int mvid;
	int size;
	int value;
	int lock_index;
	int cv_index;
	int mv_index;
	long long timestamp;
	
	printf("%s\n", "Nachos Server Start...");
    
	while (true) {
		// wait for a request
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buffer);
		// parse the request
		mutex.P();
		int id;
		DEBUG('N',"%d\n", (int)(*buffer));
		switch (*buffer) {
		case 11:
			// create lock
			// lock name
			// parse argv
			ccopy(name, buffer+2, *(buffer+1));
			name[(int)*(buffer+1)] = 0;
			lock_index = FindLock(name);
			// timestamp = atoi(buffer+(3+*(buffer+1)), *(buffer+(2+*(buffer+1))));
			timestamp = GetTime();
			// printf("Recieve request create lock %s at time %lld from %d:%d\n", name, timestamp, inPktHdr.from, inMailHdr.from);
			cout << "Recieve request create lock " << name << " at time " 
			<< timestamp << " from " << inPktHdr.from << ":" << inMailHdr.from << endl;
			if (lock_index == -1 && numberOfServer > 1) {
				// not in the table
				// ask other servers
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 1;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 0;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				commandQueue->name = new char[MaxMailSize];
				ccopy(commandQueue->name, name, strlen(name)+1);
				commandQueue->timestamp = timestamp;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				
				{
					NotifyQueue *temp = notifyQueue;
					notifyQueue = new NotifyQueue;
					notifyQueue->type = 1;
					notifyQueue->name = new char[MaxMailSize];
					ccopy(notifyQueue->name, name, strlen(name)+1);
					notifyQueue->mid = inPktHdr.from;
					notifyQueue->mb = inMailHdr.from;
					notifyQueue->from = -1;
					notifyQueue->command = -1;
					notifyQueue->next = temp;
				}
				
				int len1 = buffer[1]+2;
				ccopy(message+4, buffer, len1);
				int len2 = itoa(timestamp, message + 4 + len1 + 1);
				message[4+len1] = len2;
				printf("Len 2 %d.\n", len2);
				outMailHdr.length = 4 + len1 + len2 + 1;
				outMailHdr.to = 1;
				printf("No lock name %s, forward request to other %d servers.\n", name, numberOfServer);
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						printf("Send to server %d.\n", i);
						outPktHdr.to = i;
						outMailHdr.to = 1;
						if (!postOffice->Send(outPktHdr, outMailHdr, message)) {
							printf("Failed.\n");
						}
					}
				}
			} else if (lock_index == -1 && numberOfServer == 1) {
				// only one server
				lockid = CreateLock(name, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(lockid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// register
				lockid = CreateLock(lock_index, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(lockid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			}
			break;
		case 12:
			// delete lock
			lockid = (int)buffer[1]*100+(int)buffer[2];
			lock_index = FindLock(lockid);
			if (lock_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				outMailHdr.length = 7;
				outMailHdr.to = 1;
								
				
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				// in this server
				DestroyLock(lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 13:
			// acquire lock
			lockid = (int)buffer[1]*100+(int)buffer[2];
			lock_index = FindLock(lockid);
			printf("Acquire lock %d.\n",lockid);
			if (lock_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				printf("Not found lock %d.\n", lockid);
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = lockid / 100;
				message[6] = lockid % 100;
				outMailHdr.length = 7;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				Acquire(lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 14:
			// release lock
			DEBUG('N', "Release.\n");
			lockid = (int)buffer[1]*100+(int)buffer[2];
			lock_index = FindLock(lockid);
			printf("Release lock %d.\n",lockid);
			if (lock_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				printf("Not found lock %d.\n", lockid);
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				outMailHdr.length = 7;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				Release(lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 15:
			// create condition
			// condition name
			// parse argv
			ccopy(name, buffer+2, *(buffer+1));
			name[(int)*(buffer+1)] = 0;
			cv_index = FindCV(name);
			// timestamp = atoi(buffer+(3+*(buffer+1)), *(buffer+(2+*(buffer+1))));
			timestamp = GetTime();
			// printf("Recieve request create cv %s at time %lld from %d:%d\n", name, timestamp, inPktHdr.from, inMailHdr.from);
			cout << "Recieve request create cv " << name << " at time " 
			<< timestamp << " from " << inPktHdr.from << ":" << inMailHdr.from << endl;
			
			if (cv_index == -1 && numberOfServer > 1) {
				// not in the table
				// ask other servers
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 2;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				commandQueue->name = new char[MaxMailSize];
				ccopy(commandQueue->name, name, strlen(name)+1);
				commandQueue->timestamp = timestamp;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				
				{
					NotifyQueue *temp = notifyQueue;
					notifyQueue = new NotifyQueue;
					notifyQueue->type = 2;
					notifyQueue->name = new char[MaxMailSize];
					ccopy(notifyQueue->name, name, strlen(name)+1);
					notifyQueue->mid = inPktHdr.from;
					notifyQueue->mb = inMailHdr.from;
					notifyQueue->from = -1;
					notifyQueue->command = -1;
					notifyQueue->next = temp;
				}
				
				int len1 = buffer[1]+2;
				ccopy(message+4, buffer, len1);
				int len2 = itoa(timestamp, message + 4 + len1 + 1);
				message[4+len1] = len2;
				outMailHdr.length = 4 + len1 + len2 + 1;
				outMailHdr.to = 1;
				
				// outMailHdr.length = *(buffer+1)+7+*(buffer+2+*(buffer+1));
				// ccopy(message+4, buffer, outMailHdr.length-4);
				outMailHdr.to = 1;
				printf("No CV name %s, forward request to other %d servers.\n", name, numberOfServer);
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						outMailHdr.to = 1;
						if (!postOffice->Send(outPktHdr, outMailHdr, message)) {
							printf("Failed.\n");
						}
					}
				}
			} else if (cv_index == -1 && numberOfServer == 1) {
				// register
				cvid = CreateCondition(name, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(cvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// register
				cvid = CreateCondition(cv_index, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(cvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			}
			break;
		case 16:
			// Destroy condition
			cvid = (int)buffer[1]*100 + (int)buffer[2];
			cv_index = FindCV(cvid);
			if (cv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				outMailHdr.length = 7;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				// in this server
				DestroyCV(cv_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 17:
			// wait
			cvid = (int)buffer[1]*100 + (int)buffer[2];
			lockid = (int)buffer[3]*100 + (int)buffer[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			if (cv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				message[7] = buffer[3];
				message[8] = buffer[4];
				outMailHdr.length = 9;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else if (lock_index == -1 && numberOfServer > 1) {
				// ask for lock
				int command = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = inPktHdr.from;
				cvQueue->mb = inMailHdr.from;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(command, inPktHdr.from, inMailHdr.from, lockid, 3, i);
					}
				}
			} else {
				// in this server
				Wait(cv_index, lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 18:
			// signal
			cvid = (int)buffer[1]*100 + (int)buffer[2];
			lockid = (int)buffer[3]*100 + (int)buffer[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			printf("Signal for cv %d from %d:%d", cvid, inPktHdr.from, inMailHdr.from);
			if (cv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				printf("Signal:No CV found");
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				message[7] = buffer[3];
				message[8] = buffer[4];
				outMailHdr.length = 9;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else if (lock_index == -1 && numberOfServer > 1) {
				// ask for lock
				printf("Signal:Ask for lock");
				int command = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = inPktHdr.from;
				cvQueue->mb = inMailHdr.from;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(command, inPktHdr.from, inMailHdr.from, lockid, 4, i);
					}
				}
			} else {
				// in this server
				Signal(cv_index, lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 19:
			// broadcast
			cvid = (int)buffer[1]*100 + (int)buffer[2];
			lockid = (int)buffer[3]*100 + (int)buffer[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			if (cv_index == -1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				message[7] = buffer[3];
				message[8] = buffer[4];
				outMailHdr.length = 9;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else if (lock_index == -1 && numberOfServer > 1) { 
				// ask for lock
				int command = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = inPktHdr.from;
				cvQueue->mb = inMailHdr.from;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(command, inPktHdr.from, inMailHdr.from, lockid, 5, i);
					}
				}
			} else {
				// in this server
				Broadcast(cv_index, lock_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 21:
			// create mv
			// create condition
			// condition name
			// parse argv
			ccopy(name, buffer+4, *(buffer+3));
			name[(int)*(buffer+3)] = 0;
			mv_index = FindMV(name);
			// timestamp = atoi(buffer+(5+*(buffer+3)), *(buffer+(4+*(buffer+3))));
			timestamp = GetTime();
			// printf("Recieve request create mv %s at time %lld\n", name, timestamp);
			cout << "Recieve request create mv " << name << " at time " 
			<< timestamp << " from " << inPktHdr.from << ":" << inMailHdr.from << endl;
			size = buffer[1]*100+buffer[2];
			if (mv_index == -1 && numberOfServer > 1) {
				// not in the table
				// ask other servers
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 3;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 0;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				commandQueue->name = new char[MaxMailSize];
				ccopy(commandQueue->name, name, strlen(name)+1);
				commandQueue->timestamp = timestamp;
				commandQueue->size = size;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				
				int len1 = buffer[3]+4;
				ccopy(message+4, buffer, len1);
				int len2 = itoa(timestamp, message + 4 + len1 + 1);
				message[4+len1] = len2;
				outMailHdr.length = 4 + len1 + len2 + 1;
				outMailHdr.to = 1;
				
				// outMailHdr.length = *(buffer+3)+9+*(buffer+4+*(buffer+3));
				// outMailHdr.length = *(buffer+3)+8;
				// printf("length %d\n", outMailHdr.length);
				// ccopy(message+4, buffer, outMailHdr.length-4);
				outMailHdr.to = 1;
				{
					NotifyQueue *temp = notifyQueue;
					notifyQueue = new NotifyQueue;
					notifyQueue->type = 3;
					notifyQueue->name = new char[MaxMailSize];
					ccopy(notifyQueue->name, name, strlen(name)+1);
					notifyQueue->mid = inPktHdr.from;
					notifyQueue->mb = inMailHdr.from;
					notifyQueue->from = -1;
					notifyQueue->command = -1;
					notifyQueue->next = temp;
				}
				
				printf("No MV name %s, forward request to other %d servers.\n", name, numberOfServer);
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						printf("Send to server %d.\n", i);
						outPktHdr.to = i;
						outMailHdr.to = 1;
						if (!postOffice->Send(outPktHdr, outMailHdr, message)) {
							printf("Failed.\n");
						}
						printf("Send to server %d.\n", i);
					}
				}
			} else if (mv_index == -1 && numberOfServer == 1) {
				mvid = CreateMV(name, size, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(mvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// register
				mvid = CreateMV(mv_index, inPktHdr.from, inMailHdr.from);
				// send back
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = inMailHdr.from;
				outMailHdr.length = itoa(mvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			}
			break;
		case 22:
			// Destroy mv
			mvid = (int)buffer[1]*100 + (int)buffer[2];
			mv_index = FindMV(mvid);
			if (mv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				outMailHdr.length = 7;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				// in this server
				DestroyMV(mv_index, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 23:
			// read mv
			mvid = (int)buffer[1]*100 + (int)buffer[2];
			size = (int)buffer[3]*100 + (int)buffer[4];
			printf("Read MV.\n");
			mv_index = FindMV(mvid);
			if (mv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 0;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				message[4] = buffer[0];
				message[5] = buffer[1];
				message[6] = buffer[2];
				message[7] = buffer[3];
				message[8] = buffer[4];
				outMailHdr.length = 9;
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				// in this server
				ReadMV(mv_index, size, inPktHdr.from, inMailHdr.from);
			}
			break;
		case 24:
			// set mv
			mvid = (int)buffer[1]*100 + (int)buffer[2];
			size = (int)buffer[3]*100 + (int)buffer[4];
			value = atoi(buffer+6, *(buffer+5));
			DEBUG('N', "%d %d %d.\n",mvid, size, value);
			printf("Set MV.\n");
			mv_index = FindMV(mvid);
			if (mv_index == -1 && numberOfServer > 1) {
				// foward message to other servers
				// init message
				int command = currentCommand;
				CommandType *temp = commandQueue;
				commandQueue = new CommandType;
				commandQueue->type = 0;
				commandQueue->fail = 0;
				commandQueue->command = command;
				commandQueue->next = temp;
				commandQueue->failmessage = 1;
				commandQueue->mid = inPktHdr.from;
				commandQueue->mb = inMailHdr.from;
				currentCommand = (currentCommand+1)%10000;
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = inPktHdr.from;
				message[3] = inMailHdr.from;
				ccopy(message+4, buffer, 6+buffer[5]);
				outMailHdr.length = 10+buffer[5];
				outMailHdr.to = 1;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						// send
						outPktHdr.to = i;
						postOffice->Send(outPktHdr, outMailHdr, message);
					}
				}
			} else {
				// in this server
				SetMV(mv_index, size, value, inPktHdr.from, inMailHdr.from);
			}			
			break;
		}
		mutex.V();
	}
}

void ServerForServer(int numberOfServer) {
	PacketHeader inPktHdr, outPktHdr;
    MailHeader inMailHdr, outMailHdr;
    char buffer[MaxMailSize];
	char message[MaxMailSize];
	char name[MaxMailSize];
	int lockid;
	int cvid;
	int mvid;
	int size;
	int value;
	int lock_index;
	int cv_index;
	int mv_index;
	int offset;
	
	while(true) {
		postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
		mutex.P();
		
		int command = (int)buffer[0]*100 + (int)buffer[1];
		int mid = (int)buffer[2];
		int mb = (int)buffer[3];
		char* buf = buffer+4;
		CommandType *current;
		CommandType *prev;
		printf("New Message %d %d %d %d.\n", command, mid, mb, buffer[4]);
		
		switch(*buf) {
		case 0:
			// fail
			current = commandQueue;
			prev = NULL;
			printf("Fail message for command %d.\n", command);
			while (current) {
				if (current->command == command) {
					current->fail++;
					if (current->fail == numberOfServer - 1) {
						switch(current->type) {
						case 1:
							// create lock
							printf("Create Lock %s.\n", current->name);
							lockid = CreateLock(current->name, current->mid, current->mb);
							lock_index = FindLock(lockid);
							// send back message to mb:mid
							// handle notify queue
							outPktHdr.to = current->mid;
							outMailHdr.to = current->mb;
							outMailHdr.length = itoa(lockid, message)+1;
							message[outMailHdr.length-1] = 0;
							// postOffice->Send(outPktHdr, outMailHdr, message);
							// notify clients and servers in notify queue
							{
								NotifyQueue* cur = notifyQueue;
								NotifyQueue* pre = NULL;
								while (cur) {
									if (cur->type == current->type && !strcmp(cur->name,current->name)) {
										// send back
										outPktHdr.to = cur->mid;
										outMailHdr.to = cur->mb;
										printf("Send back lock id %d to client %d mailbox %d.\n", lockid, cur->mid, cur->mb);
										CreateLock(lock_index, cur->mid, cur->mb);
										postOffice->Send(outPktHdr, outMailHdr, message);
										if (cur->from >= 0)
											Message(cur->from, cur->command, 1);
										NotifyQueue* t = cur->next;
										if (pre != NULL) pre->next = cur->next;
										else notifyQueue = cur->next;
										delete cur;
										cur = t;
									} else {
										pre = cur;
										cur = cur->next;
									}
								}
							}
							break;
						case 2:
							// create cv
							printf("Create CV %s %d %d.\n", current->name, current->mid, current->mb);
							cvid = CreateCondition(current->name, current->mid, current->mb);
							cv_index = FindCV(cvid);
							// send back message to mb:mid
							// handle notify queue
							outPktHdr.to = current->mid;
							outMailHdr.to = current->mb;
							outMailHdr.length = itoa(cvid, message)+1;
							message[outMailHdr.length-1] = 0;
							// postOffice->Send(outPktHdr, outMailHdr, message);
							// notify clients and servers in notify queue
							{
								NotifyQueue* cur = notifyQueue;
								NotifyQueue* pre = NULL;
								while (cur) {
									if (cur->type == current->type && !strcmp(cur->name,current->name)) {
										// send back
										outPktHdr.to = cur->mid;
										outMailHdr.to = cur->mb;
										printf("Send back cv id %d to client %d mailbox %d.\n", cvid, cur->mid, cur->mb);
										CreateCondition(cv_index, cur->mid, cur->mb);
										postOffice->Send(outPktHdr, outMailHdr, message);
										printf("Send back cv id %d to client %d mailbox %d.\n", cvid, cur->mid, cur->mb);
										if (cur->from >= 0)
											Message(cur->from, cur->command, 1);
										NotifyQueue* t = cur->next;
										if (pre != NULL) pre->next = cur->next;
										else notifyQueue = cur->next;
										delete cur;
										cur = t;
									} else {
										pre = cur;
										cur = cur->next;
									}
								}
							}
							break;
						case 3:
							// create mv
							printf("Create MV %s.\n", current->name);
							// mv_index = FindMV(current->name);
							mvid = CreateMV(current->name, current->size, current->mid, current->mb);
							printf("MV %s created.\n", current->name);
							mv_index = FindMV(mvid);
							// send back message to mb:mid
							// handle notify queue
							outPktHdr.to = current->mid;
							outMailHdr.to = current->mb;
							outMailHdr.length = itoa(mvid, message)+1;
							message[outMailHdr.length-1] = 0;
							// postOffice->Send(outPktHdr, outMailHdr, message);
							// notify clients and servers in notify queue
							{
								NotifyQueue* cur = notifyQueue;
								NotifyQueue* pre = NULL;
								while (cur) {
									// printf("%s %s %d %d\n", cur->name, current->name, strlen(cur->name) ,strlen(current->name));
									if (cur->type == current->type && !strcmp(cur->name,current->name)) {
										// send back
										outPktHdr.to = cur->mid;
										outMailHdr.to = cur->mb;
										printf("Send back mv id %d to client %d mailbox %d.\n", mvid, cur->mid, cur->mb);
										CreateMV(mv_index, cur->mid, cur->mb);
										
										postOffice->Send(outPktHdr, outMailHdr, message);
										if (cur->from >= 0)
											Message(cur->from, cur->command, 1);
										NotifyQueue* t = cur->next;
										if (pre != NULL) pre->next = cur->next;
										else notifyQueue = cur->next;
										delete cur;
										cur = t;
									} else {
										pre = cur;
										cur = cur->next;
									}
								}
							}
							break;
						default:
							// send back fail message
							message[0] = current->failmessage;
							outPktHdr.to = current->mid;
							outMailHdr.to = current->mb;
							outMailHdr.length = 1;
							postOffice->Send(outPktHdr, outMailHdr, message);
							break;
						}
						if (prev == NULL) {
							commandQueue = current->next;
						} else {
							prev->next = current->next;
						}
						delete current;
					}
					break;
				}
				prev = current;
				current = current->next;
			}
			break;
		case 1:
			// success
			current = commandQueue;
			prev = NULL;
			while (current) {
				if (current->command == command) {
					if (prev == NULL) {
						commandQueue = current->next;
					} else {
						prev->next = current->next;
					}
					break;
				}
				prev = current;
				current = current->next;
			}
			if (current->type) {
				// remove node from notify queue
				NotifyQueue* cur = notifyQueue;
				NotifyQueue* pre = NULL;
				while (cur) {
					if (cur->type == current->type && !strcmp(cur->name,current->name)) {
						NotifyQueue* t = cur->next;
						if (pre == NULL) notifyQueue = cur->next;
						else pre->next = cur->next;
						delete cur;
						cur = t;
					} else {
						pre = cur;
						cur = cur->next;
					}
				}
			}
			delete current;
			break;
		case 2:
			// test lock
			lockid = (int)buf[1]*100+(int)buf[2];
			lock_index = FindLock(lockid);
			printf("Test Lock %d from server %d\n", lockid, inPktHdr.from);
			if (lock_index == -1) {
				// fail
				Message(inPktHdr.from, mid, mb, command, 9);
			} else {
				if (TestLockHolder(lock_index, mid, mb)) {
					// success
					int reply = buf[3];
					Message(inPktHdr.from, mid, mb, command, reply);
				} else {
					// fail
					Message(inPktHdr.from, mid, mb, command, 9);
				}
			}
			break;
		case 3:
			// server wait
			{
				// put mid:mb in wait queue
				printf("Condition Server Wait.\n");
				ConditionQueue *c = cvQueue;
				ConditionQueue *p = NULL;
				while (c) {
					if (c->command == command) {
						cvid = c->cvid;
						lockid = c->lockid;
						int id = mid * MAX_MAILBOX + mb;
						CVQueueElement *t = new CVQueueElement;
						t->id = id;
						t->lock = lockid;
						cvs[cvid].queue->Append((void*)t);
						
						if (p == NULL) cvQueue = c->next;
						else p->next = c->next;
						delete c;
						 
						break;
					}
					p = c;
					c = c->next;
				}
				
				// release the lock
				message[0] = command / 100;
				message[1] = command % 100;
				message[2] = mid;
				message[3] = mb;
				message[4] = 6;
				message[5] = lockid / 100;
				message[6] = lockid % 100;
				outPktHdr.to = inPktHdr.from;
				outMailHdr.to = 1;
				outMailHdr.length = 7;
				printf("%d %d %d %d\n", command, mid, mb, lockid);
				postOffice->Send(outPktHdr, outMailHdr, message);
			}
			break;
		case 4:
		{
			// server signal
			// pull a client out of wait queue
			// acquire the client
			printf("Condition Server Signal.\n");
			ConditionQueue *c = cvQueue;
			ConditionQueue *p = NULL;
			while (c) {
				if (c->command == command) {
					cvid = c->cvid;
					lockid = c->lockid;
					int id = mid * MAX_MAILBOX + mb;
					
					CVQueueElement *t = (CVQueueElement *)cvs[cvid].queue->Remove();
					List *l = new List;
					if (t != NULL) {
						// match the lock
						while (t->lock != lockid) {
							l->Append((void*)t);
							t = (CVQueueElement *)cvs[cvid].queue->Remove();
							if (t == NULL) break;
						}
						void *tt = l->Remove();
						while (tt != NULL) {
							cvs[cvid].queue->Prepend(tt);
							tt = l->Remove();
						}
						if (t != NULL) {
							DEBUG('L', "Wake one Up.\n");
							int v = t->id;
							// Acquire(lockid, v / MAX_MAILBOX, v % MAX_MAILBOX);
							
							// Acquire the lock
							message[0] = command / 100;
							message[1] = command % 100;
							message[2] = v / MAX_MAILBOX;
							message[3] = v % MAX_MAILBOX;
							message[4] = 7;
							message[5] = lockid / 100;
							message[6] = lockid % 100;
							outPktHdr.to = inPktHdr.from;
							outMailHdr.to = 1;
							outMailHdr.length = 7;
							postOffice->Send(outPktHdr, outMailHdr, message);
							c->success = 1;
							delete t;
						}
					}
					delete l;
					if (!c->success) {
						message[0] = 0;
						outPktHdr.to = c->mid;
						outMailHdr.to = c->mb;
						outMailHdr.length = 1;
						postOffice->Send(outPktHdr, outMailHdr, message);
						if (p == NULL) cvQueue = c->next;
						else p->next = c->next;
						delete c;
					}
					break;
				}
				p = c;
				c = c->next;
			}
			
		}
		break;		
		case 5:
			// server broadcast
			// pull all clients out of wait queue
			// acquire the client
			{
				// server signal
				// pull a client out of wait queue
				// acquire the client
				ConditionQueue *c = cvQueue;
				ConditionQueue *p = NULL;
				while (c) {
					if (c->command == command) {
						cvid = c->cvid;
						lockid = c->lockid;
						int id = mid * MAX_MAILBOX + mb;
						c->success = 0;
						
						CVQueueElement *t = (CVQueueElement *)cvs[cvid].queue->Remove();
						List *l = new List;
						while (t != NULL) {
							if (t->lock != lockid) {
								l->Append((void*)t);
							} else {
								int v = t->id;
								// Acquire(lockid, v / MAX_MAILBOX, v % MAX_MAILBOX);
								
								// Acquire the lock
								message[0] = command / 100;
								message[1] = command % 100;
								message[2] = v / MAX_MAILBOX;
								message[3] = v % MAX_MAILBOX;
								message[4] = 7;
								message[5] = lockid / 100;
								message[6] = lockid % 100;
								outPktHdr.to = inPktHdr.from;
								outMailHdr.to = 1;
								outMailHdr.length = 7;
								postOffice->Send(outPktHdr, outMailHdr, message);
								c->success++;
								
								delete t;
							}
							t = (CVQueueElement *)cvs[cvid].queue->Remove();
						}
						void *tt = l->Remove();
						while (tt != NULL) {
							cvs[cvid].queue->Prepend(tt);
							tt = l->Remove();
						}
						delete l;
						if (!c->success) {
							message[0] = 0;
							outPktHdr.to = c->mid;
							outMailHdr.to = c->mb;
							outMailHdr.length = 1;
							postOffice->Send(outPktHdr, outMailHdr, message);
							if (p == NULL) cvQueue = c->next;
							else p->next = c->next;
							delete c;
						}
						break;
					}
					p = c;
					c = c->next;
				}

			}
			break;
		case 6:
			// server release
			printf("Condition Release.\n");
			lockid = buf[1]*100 + buf[2];
			lock_index = FindLock(lockid);
			ReleaseWithoutConfirm(lock_index, mid, mb);
			// Message(inPktHdr.from, command, 7);
			break;
		case 7:
			// server acquire
			printf("Condition Acquire.\n");
			lockid = buf[1]*100 + buf[2];
			lock_index = FindLock(lockid);
			Acquire(lock_index, mid, mb);
			Message(inPktHdr.from, mid, mb, command, 8);
			break;
		case 8:
			// server confirm back
			// check the success equals to confirm
			// get rid of the node
			{
				ConditionQueue *c = cvQueue;
				ConditionQueue *p = NULL;
				while (c) {
					if (c->command == command) {
						c->confirm++;
						if (c->confirm == c->success) {
							message[0] = 0;
							outPktHdr.to = c->mid;
							outMailHdr.to = c->mb;
							outMailHdr.length = 1;
							postOffice->Send(outPktHdr, outMailHdr, message);
							if (p == NULL) cvQueue = c->next;
							else p->next = c->next;
							delete c;
						}
						break;
					}
					p = c;
					c = c->next;
				}

			}
			break;
		case 9:
			// lock fail
			// check the fail equals to numberOfServer - 1
			// get rid of the node
			{
				printf("Lock Fail.\n");
				ConditionQueue *c = cvQueue;
				ConditionQueue *p = NULL;
				while (c) {
					if (c->command == command) {
						c->fail++;
						if (c->fail == numberOfServer - 1) {
							message[0] = 1;
							outPktHdr.to = c->mid;
							outMailHdr.to = c->mb;
							outMailHdr.length = 1;
							postOffice->Send(outPktHdr, outMailHdr, message);
							if (p == NULL) cvQueue = c->next;
							else p->next = c->next;
							delete c;
						}
						break;
					}
					p = c;
					c = c->next;
				}
			}
			break;
		case 11:
			// create lock
			ccopy(name, buf+2, *(buf+1));
			name[(int)*(buf+1)] = 0;
			printf("Request from server for create lock name %s.\n", name);
			lock_index = FindLock(name);
			if (lock_index != -1) {
				// send the lock index to client
				outPktHdr.to = mid;
				outMailHdr.to = mb;
				lockid = CreateLock(lock_index, mid, mb);
				outMailHdr.length = itoa(lockid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// send back fail message
				int len = (int)*(buf+1);
				buf += len + 2;
				long long req_time = atoi(buf+1 , (int)*buf, 0);
				// printf("No lock found at time %lld.\n", req_time);
				cout << "No lock found at time " << req_time << endl;
				// search for the node
				current = commandQueue;
				while (current != NULL) {
					if (current->type == 1 &&
						!strcmp(current->name, name) &&
						(current->timestamp < req_time && !(current->timestamp < -1000000000 && req_time > 1000000000)||
						(req_time < -1000000000 && current->timestamp > 1000000000) ||
						(current->timestamp == req_time && current->mid < mid) || 
						(current->timestamp == req_time && current->mid == mid && current->mb < mb))) {
							// do not handle the request
							// put the request into notify queue
							NotifyQueue *temp = notifyQueue;
							notifyQueue = new NotifyQueue;
							notifyQueue->type = 1;
							notifyQueue->name = new char[MaxMailSize];
							ccopy(notifyQueue->name, name, strlen(name)+1);
							notifyQueue->mid = mid;
							notifyQueue->mb = mb;
							notifyQueue->from = inPktHdr.from;
							notifyQueue->command = command;
							notifyQueue->next = temp;
							break;
					}
					current = current->next;
				}
				if (current == NULL) {
					// send back no response
					printf("Send back no message.\n");
					Message(inPktHdr.from, command, 0);
				}
			}
			break;
		case 12:
			// delete
			lockid = (int)buf[1]*100+(int)buf[2];
			lock_index = FindLock(lockid);
			if (lock_index != -1) {
				// Destroy the lock
				DestroyLock(lock_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 13:
			// Acquire
			lockid = (int)buf[1]*100+(int)buf[2];
			lock_index = FindLock(lockid);
			printf("Server Acquire %d.\n", lockid);
			if (lock_index != -1) {
				// Acquire the lock
				printf("Found Lock %d.\n", lockid);
				printf("ACQURE %d.\n",lock_index);
				Acquire(lock_index, mid, mb);
				Message(inPktHdr.from, command,1);
			} else {
				// send back fail message to server
				printf("Not Found Lock %d.\n", lockid);
				Message(inPktHdr.from, command,0);
			}
			break;
		case 14:
			// Release
			lockid = (int)buf[1]*100+(int)buf[2];
			lock_index = FindLock(lockid);
			printf("Server Release %d.\n", lockid);
			if (lock_index != -1) {
				// Release the lock
				printf("Found Lock %d.\n", lockid);
				Release(lock_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found Lock %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 15:
			// create cv
			ccopy(name, buf+2, *(buf+1));
			name[(int)*(buf+1)] = 0;
			printf("Request from server for create cv name %s.\n", name);
			cv_index = FindCV(name);
			if (cv_index != -1) {
				// send the lock index to client
				outPktHdr.to = mid;
				outMailHdr.to = mb;
				cvid = CreateCondition(cv_index, mid, mb);
				outMailHdr.length = itoa(cvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// send back fail message
				int len = (int)*(buf+1);
				buf += len + 2;
				long long req_time = atoi(buf+1 , (int)*buf, 0);
				printf("No cv found.\n");
				// search for the node
				current = commandQueue;
				while (current != NULL) {
					if (current->type == 2 &&
						!strcmp(current->name, name) &&
						(current->timestamp < req_time && !(current->timestamp < -1000000000 && req_time > 1000000000)||
						(req_time < -1000000000 && current->timestamp > 1000000000) ||
						(current->timestamp == req_time && current->mid < mid) || 
						(current->timestamp == req_time && current->mid == mid && current->mb < mb))) {
							// do not handle the request
							// put the request into notify queue
							NotifyQueue *temp = notifyQueue;
							notifyQueue = new NotifyQueue;
							notifyQueue->type = 2;
							notifyQueue->name = new char[MaxMailSize];
							ccopy(notifyQueue->name, name, strlen(name)+1);
							notifyQueue->mid = mid;
							notifyQueue->mb = mb;
							notifyQueue->from = inPktHdr.from;
							notifyQueue->command = command;
							notifyQueue->next = temp;
							break;
						}
						current = current->next;
					}
					if (current == NULL) {
					// send back no response
						printf("Send back no message.\n");
						Message(inPktHdr.from, command, 0);
					}
				}
			break;
		case 16:
			// destroy cv
			cvid = (int)buf[1]*100+(int)buf[2];
			cv_index = FindCV(cvid);
			printf("Server Destory CV %d.\n", lockid);
			if (cv_index != -1) {
				// Release the lock
				printf("Found CV %d.\n", lockid);
				DestroyCV(cv_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found CV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 17:
			// wait
			cvid = (int)buf[1]*100+(int)buf[2];
			lockid = (int)buf[3]*100 + (int)buf[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			printf("Server Wait CV %d.\n", lockid);
			if (cv_index != -1 && lock_index != -1) {
				// Release the lock
				printf("Found CV %d.\n", lockid);
				Wait(cv_index, lock_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else if (cv_index != -1) {
				// ask for lock
				printf("Ask for Lock %d.\n", lockid);
				int comm = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = mid;
				cvQueue->mb = mb;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(comm, mid, mb, lockid, 3, i);
					}
				}
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found CV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 18:
			// signal
			cvid = (int)buf[1]*100+(int)buf[2];
			lockid = (int)buf[3]*100 + (int)buf[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			printf("Server Destory CV %d.\n", lockid);
			if (cv_index != -1 && lock_index != -1) {
				// Release the lock
				printf("Found CV %d.\n", lockid);
				Signal(cv_index, lock_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else if (cv_index != -1) {
				// ask for lock
				int comm = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = mid;
				cvQueue->mb = mb;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(comm, mid, mb, lockid, 4, i);
					}
				}
				Message(inPktHdr.from, command, 1);
			}else {
				// send back fail message to server
				printf("Not Found CV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 19:
			// broadcast
			cvid = (int)buf[1]*100+(int)buf[2];
			lockid = (int)buf[3]*100 + (int)buf[4];
			cv_index = FindCV(cvid);
			lock_index = FindLock(lockid);
			printf("Server Destory CV %d.\n", lockid);
			if (cv_index != -1 && lock_index != -1) {
				// Release the lock
				printf("Found CV %d.\n", lockid);
				Broadcast(cv_index, lock_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else if (cv_index != -1) {
				// ask for lock
				int comm = currentCommand;
				ConditionQueue *temp = cvQueue;
				cvQueue = new ConditionQueue;
				cvQueue->command = currentCommand;
				cvQueue->cvid = cv_index;
				cvQueue->lockid = lockid;
				cvQueue->fail = 0;
				cvQueue->success = 0;
				cvQueue->confirm = 0;
				cvQueue->mid = mid;
				cvQueue->mb = mb;
				cvQueue->next = temp;
				currentCommand = (currentCommand+1)%10000;
				for(int i = 0; i < numberOfServer; i++) {
					if (i != server) {
						LockMessage(comm, mid, mb, lockid, 5, i);
					}
				}
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found CV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 21:
			// create mv
			ccopy(name, buf+4, *(buf+3));
			name[buf[3]] = 0;
			printf("Request from server for create MV name %s.\n", name);
			name[(int)*(buf+3)] = 0;
			mv_index = FindMV(name);
			if (mv_index != -1) {
				// send the lock index to client
				outPktHdr.to = mid;
				outMailHdr.to = mb;
				mvid = CreateMV(mv_index, mid, mb);
				outMailHdr.length = itoa(mvid, message)+1;
				message[outMailHdr.length-1] = 0;
				postOffice->Send(outPktHdr, outMailHdr, message);
			} else {
				// send back fail message
				int len = (int)*(buf+3);
				buf += len + 4;
				long long req_time = atoi(buf+1 , (int)*buf, 0);
				// printf("No mv found at time %lld.\n", req_time);
				cout << "No MV found at time " << req_time << endl;
				// search for the node
				current = commandQueue;
				while (current != NULL) {
					if (current->type == 3 &&
						!strcmp(current->name, name) &&
						(current->timestamp < req_time && !(current->timestamp < -1000000000 && req_time > 1000000000)||
						(req_time < -1000000000 && current->timestamp > 1000000000) ||
						(current->timestamp == req_time && current->mid < mid) || 
						(current->timestamp == req_time && current->mid == mid && current->mb < mb))) {
							// do not handle the request
							// put the request into notify queue
							NotifyQueue *temp = notifyQueue;
							notifyQueue = new NotifyQueue;
							notifyQueue->type = 3;
							notifyQueue->name = new char[MaxMailSize];
							ccopy(notifyQueue->name, name, strlen(name)+1);
							notifyQueue->mid = mid;
							notifyQueue->mb = mb;
							notifyQueue->from = inPktHdr.from;
							notifyQueue->command = command;
							notifyQueue->next = temp;
							break;
						}
						current = current->next;
					}
					if (current == NULL) {
					// send back no response
						printf("Send back no message.\n");
						Message(inPktHdr.from, command, 0);
					}
				}
			
			break;
		case 22:
			mvid = (int)buf[1]*100+(int)buf[2];
			mv_index = FindMV(mvid);
			printf("Server Destroy MV %d.\n", lockid);
			if (mv_index != -1) {
				// Release the lock
				printf("Found MV %d.\n", lockid);
				DestroyMV(mv_index, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found MV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 23:
			mvid = (int)buf[1]*100+(int)buf[2];
			offset = (int)buf[3]*100+(int)buf[4];
			mv_index = FindMV(mvid);
			printf("Server Read %d.\n", lockid);
			if (mv_index != -1) {
				// Release the lock
				printf("Found MV %d.\n", lockid);
				ReadMV(mv_index, offset, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found MV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		case 24:
			mvid = (int)buf[1]*100+(int)buf[2];
			offset = (int)buf[3]*100+(int)buf[4];
			mv_index = FindMV(mvid);
			printf("Server Release %d.\n", lockid);
			if (mv_index != -1) {
				// Release the lock
				printf("Found MV %d.\n", lockid);
				value = atoi(buf+6, *(buf+5));
				SetMV(mv_index, offset, value, mid, mb);
				Message(inPktHdr.from, command, 1);
			} else {
				// send back fail message to server
				printf("Not Found MV %d.\n", lockid);
				Message(inPktHdr.from, command, 0);
			}
			break;
		}
		mutex.V();
	}
}

time_t lastTime = 0;

// pining thread
void PingThread(int dummy) {
	PacketHeader outPktHdr;
	MailHeader outMailHdr;
	char message[MaxMailSize] = "Hello World.";
	outMailHdr.length = 13;
	outMailHdr.to = 0;
	time_t currentTime = time(NULL);
	if (currentTime-lastTime >= 1) {
		// check time
		lastTime = currentTime;
		mutex.P();
		// check lock holder
		for(int i = 0; i < lockCount; i++) {
			int id = locks[i].holder;
			int mid = id / MAX_MAILBOX;
			int mb = id % MAX_MAILBOX;
			if (id > 0) {
				outPktHdr.to = mid;
				if (!postOffice->Send(outPktHdr, outMailHdr, message)) {
					// lock holder fails
					ReleaseWithoutConfirm(i, mid, mb);
				}
			}
		}
		mutex.V();
	}
}
